use sea_orm::{DatabaseConnection, DbErr, EntityTrait, QueryFilter, ColumnTrait};
use chrono::Utc;
use crate::models::rbac::menu::{Entity as MenuEntity, Model as Menu, ActiveModel as MenuActiveModel};
use crate::models::rbac::menu::Column;

pub struct MenuService;

impl MenuService {
    pub async fn create(
        db: &DatabaseConnection,
        name: &str,
        path: &str,
        component: &str,
        icon: &str,
        parent_id: Option<&str>,
        order_num: i32,
        tenant_id: i64,
    ) -> Result<Menu, DbErr> {
        let now = Utc::now().naive_utc();
        let menu = MenuActiveModel {
            name: sea_orm::Set(name.to_string()),
            path: sea_orm::Set(Some(path.to_string())),
            component: sea_orm::Set(Some(component.to_string())),
            icon: sea_orm::Set(Some(icon.to_string())),
            parent_id: sea_orm::Set(parent_id.map(|s| s.to_string())),
            order_num: sea_orm::Set(order_num),
            tenant_id: sea_orm::Set(Some(tenant_id)),
            create_time: sea_orm::Set(now),
            update_time: sea_orm::Set(now),
            ..Default::default()
        };

        MenuEntity::insert(menu).exec(db).await?;
        MenuEntity::find()
            .filter(Column::Name.eq(name))
            .filter(Column::TenantId.eq(tenant_id))
            .one(db)
            .await?
            .ok_or_else(|| {
                tracing::error!("Menu not found");
                DbErr::RecordNotFound("Menu not found".to_string())
            })
    }

    pub async fn update(
        db: &DatabaseConnection,
        id: &str,
        name: Option<&str>,
        path: Option<&str>,
        component: Option<&str>,
        icon: Option<&str>,
        parent_id: Option<&str>,
        order_num: Option<i32>,
        tenant_id: i64,
    ) -> Result<Menu, DbErr> {
        let mut menu: MenuActiveModel = MenuEntity::find_by_id(id)
            .filter(Column::TenantId.eq(tenant_id))
            .one(db)
            .await?
            .ok_or(DbErr::RecordNotFound("Menu not found".to_string()))?
            .into();

        if let Some(name) = name {
            menu.name = sea_orm::Set(name.to_string());
        }
        if let Some(path) = path {
            menu.path = sea_orm::Set(Some(path.to_string()));
        }
        if let Some(component) = component {
            menu.component = sea_orm::Set(Some(component.to_string()));
        }
        if let Some(icon) = icon {
            menu.icon = sea_orm::Set(Some(icon.to_string()));
        }
        if let Some(parent_id) = parent_id {
            menu.parent_id = sea_orm::Set(Some(parent_id.to_string()));
        }
        if let Some(order_num) = order_num {
            menu.order_num = sea_orm::Set(order_num);
        }
        menu.update_time = sea_orm::Set(Utc::now().naive_utc());

        MenuEntity::update(menu).exec(db).await?;
        MenuEntity::find_by_id(id)
            .filter(Column::TenantId.eq(tenant_id))
            .one(db)
            .await?
            .ok_or_else(|| {
                tracing::error!("Menu not found");
                DbErr::RecordNotFound("Menu not found".to_string())
            })
    }

    pub async fn delete(db: &DatabaseConnection, id: &str, tenant_id: i64) -> Result<u64, DbErr> {
        let result = MenuEntity::delete_by_id(id)
            .filter(Column::TenantId.eq(tenant_id))
            .exec(db)
            .await?;
        Ok(result.rows_affected)
    }

    pub async fn find_by_id(db: &DatabaseConnection, id: &str, tenant_id: i64) -> Result<Menu, DbErr> {
        MenuEntity::find_by_id(id)
            .filter(Column::TenantId.eq(tenant_id))
            .one(db)
            .await?
            .ok_or(DbErr::RecordNotFound("Menu not found".to_string()))
    }

    pub async fn find_all(db: &DatabaseConnection, tenant_id: i64) -> Result<Vec<Menu>, DbErr> {
        MenuEntity::find()
            .filter(Column::TenantId.eq(tenant_id))
            .all(db)
            .await
    }

    pub async fn find_tree(db: &DatabaseConnection, tenant_id: i64) -> Result<Vec<Menu>, DbErr> {
        let menus = MenuEntity::find()
            .filter(Column::TenantId.eq(tenant_id))
            .all(db)
            .await?;
        Ok(menus)
    }
}
